#
# srt.rb
#
# Cucumber proxy formatter which records a screen cast for each scenario
# Nested formatter can be specified in coffiguration file
#

require 'cucumber/formatter/progress'
require 'cucumber/step_definition_light'
require 'rbconfig'
require 'pathname'
require 'fileutils'

require 'recorders/recorder_win.rb'
require 'cfgtools.rb'

$min_fail_step_duration = Float(get_config_value("min_fail_step_duration", 3.0))
$min_skip_step_duration = Float(get_config_value("min_skip_step_duration", 0.5))
$pass_step_color = get_config_value("pass_step_color", "#008000")
$pass_arg_color = get_config_value("pass_arg_color", "#00FF00")
$fail_step_color = get_config_value("fail_step_color", "#800000")
$fail_arg_color = get_config_value("fail_arg_color", "#FF0000")
$skip_step_color = get_config_value("skip_step_color", "#808000")
$skip_arg_color = get_config_value("skip_arg_color", "#FFFF00")
$report_path = get_config_value("report_path", "report")
$delete_successful = get_config_value("delete_successful", false) == true
$html_header = get_config_value("html_header", "data/header.html")
$html_footer = get_config_value("html_footer", "data/footer.html")
$nested_formatter_class = get_config_value("nested_formatter")

$recorder = nil
$osname = RbConfig::CONFIG['target_os']
if $osname == 'mswin' or $osname == 'mingw32'
    $recorder = RuSalad::Recorder::Win.new()
end
fail "Failed to find screencast recorder for os '#{$osname}'" unless $recorder

$nested_formatter = nil

module RuSalad
  module Formatter
    class Srt
      def initialize(step_mother, io, options)
        if $nested_formatter_class
          $nested_formatter = eval "#{$nested_formatter_class}.new(step_mother, io, options)"
        end
        @io = io
        @indent = 0
        @video_id = 0
      end

      def respond_to?(*args)
        true
      end

      def before_feature(feature)
        safename = String(feature.file).gsub(/.*[\\\/]/, "")
        safename = safename.gsub(/\..*/, "")
        @feature_path = File.join($report_path, safename)
        @feature_safename = safename
        @is_background = false
        @stepno = 1

        FileUtils.rm_rf @feature_path
        FileUtils.mkpath @feature_path

        $nested_formatter.before_feature(feature) if $nested_formatter
      end

      def before_background(background)
        @video_id = @video_id + 1
        $nested_formatter.before_background(background) if $nested_formatter and $nested_formatter.class.method_defined? 'before_background'
        @is_background = true

        begin
          $video_error = nil
          $recorder.start(@feature_path, "rusalad-feature-tempvideo-#{@video_id}", @feature_safename)
        rescue Exception => e
          $video_error = e
        end

        @subtitleNo = 1
        @scenarioStart = Time.now.to_f
      end

      def before_feature_element(feature_element)
        @video_id = @video_id + 1
        $nested_formatter.before_feature_element(feature_element) if $nested_formatter
        if @is_background
          @is_background = false
        else
          begin
            $video_error = nil
            $recorder.start(@feature_path, "rusalad-feature-tempvideo-#{@video_id}", @feature_safename)
          rescue Exception => e
            $video_error = e
          end

          @subtitleNo = 1
          @scenarioStart = Time.now.to_f
        end

        safename = feature_element.name.gsub(/[\\\/\.]/, " ")
        @scenariofilename = File.join(@feature_path, "#{safename}");
      end

      def after_feature_element(feature_element)
        scendur = Time.now.to_f - @scenarioStart
        if $video_error == nil
          begin
            $recorder.stop
            if feature_element.failed? || !$delete_successful

              limit=3
              @moviefilename=""
              while ((limit > 0) && !(File.exists?("#{@moviefilename}.mkv")))
                package_video(feature_element)
                limit = limit - 1
              end
              FileUtils.mv "#{@feature_path}/rusalad-feature-tempvideo-#{@video_id}.srt", "#{@moviefilename}.srt"
              if (File.exists?("#{@moviefilename}.mkv"))
                FileUtils.rm_f "#{@feature_path}/rusalad-feature-tempvideo-#{@video_id}264.avi"
                FileUtils.rm_f "#{@feature_path}/rusalad-feature-tempvideo-#{@video_id}.avi"
              end
            else
              FileUtils.rm_f "#{@feature_path}/rusalad-feature-tempvideo-#{@video_id}.avi"
            end
          rescue Exception => e
            $video_error = e
          end
        end
        if $nested_formatter
          if $nested_formatter.class.method_defined? 'after_feature_element_ext'
            $nested_formatter.after_feature_element_ext(feature_element, Pathname.new("#{@moviefilename}").basename, $video_error)
          else
            $nested_formatter.after_feature_element(feature_element) if $nested_formatter
          end
        end
      end

      def before_step(step)
        $nested_formatter.before_step(step) if $nested_formatter
        @stepStart = Time.now.to_f
        scendur = Time.now.to_f - @scenarioStart
        @stepStartRel = scendur
      end

      def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_column_line)
        $recorder.step

        if $nested_formatter
          if $nested_formatter.class.method_defined? 'after_step_result_ext'
            screenshot = "#{@feature_safename}_#{@stepno}.png"
            fn = File.join(@feature_path, screenshot)
            screenshot = nil unless File.exists?(fn)
            $nested_formatter.after_step_result_ext(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_column_line, screenshot)
          else
            $nested_formatter.after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_column_line)
          end
        end
        stepdur = Time.now.to_f - @stepStart
        if status == :failed && stepdur < $min_fail_step_duration
          sleep $min_fail_step_duration - stepdur
        end
        if status == :skipped && stepdur < $min_skip_step_duration
          sleep $min_skip_step_duration - stepdur
        end
        scendur = Time.now.to_f - @scenarioStart

        print "#{@subtitleNo}"
        @subtitleNo = @subtitleNo + 1
        print "#{format_time(@stepStartRel)} --> #{format_time(scendur)}"
        if status == :skipped
          args = step_match.format_args("<font color=" "#{$skip_arg_color}" ">%s</font>")
          text = "<font color=" "#{$skip_step_color}" ">[Skipped] #{keyword}#{args}</font>"
        else
          if status == :failed
            args = step_match.format_args("<font color=" "#{$fail_arg_color}" ">%s</font>")
            text = "<font color=" "#{$fail_step_color}" ">[Failed] #{keyword}#{args}</font>"
          else
            args = step_match.format_args("<font color=" "#{$pass_arg_color}" ">%s</font>")
            text = "<font color=" "#{$pass_step_color}" ">[Passed] #{keyword}#{args}</font>"
          end
        end
        print text
        print ""

        @stepno = @stepno + 1
      end

      def method_missing(name, *args)
        if $nested_formatter.class.method_defined? name
          $nested_formatter.method(name).call args
        else
          m = $nested_formatter.method(:method_missing)
          if $nested_formatter.class.method_defined? :method_missing
            $nested_formatter.method(:method_missing).call name, args if $nested_formatter.method_defined?(:method_missing)
          end
        end
      end

      private

      def format_time(time)
        hours = "%02d" % ((time / 3600).to_i)
        minutes = "%02d" % ((time / 60).to_i % 60)
        seconds = "%02d" % (time.to_i % 60)
        milliseconds = "%03d" % (((time - time.to_i) * 1000).to_i)
        return "#{hours}:#{minutes}:#{seconds},#{milliseconds}"
      end

      def package_video(feature_element)
        prefix = feature_element.failed? ? "Fail" : "Pass"
        safename = feature_element.name.gsub(/[\\\/\.]/, " ")
        moviename = "#{prefix} - #{safename}"
        @moviefilename = File.join(@feature_path, moviename); 
        $recorder.package(@feature_path, "rusalad-feature-tempvideo-#{@video_id}", moviename)
      end

      def print(text)
        f = File.new("#{@feature_path}/rusalad-feature-tempvideo-#{@video_id}.srt", 'a')
        f.puts text
        f.close
      end

      def indent
        (' ' * @indent)
      end
    end
  end
end
